/**
 * 总和模块
 * 把编译模块和运行模块进行整合
 */

#pragma once
#include "Compiler.hpp"
#include "Runner.hpp"
#include <jsoncpp/json/json.h>
#include <signal.h>

namespace OnlineOJ
{
    namespace ns_CR
    {
        // 编译运行整合
        class CompileRun
        {
        public:
            /**
             * in_json:传入的Json格式的数据，包含字段：
             * code : 用户提交的代码
             * input：用户自己的输入参数
             * cpu_limit:cpu资源限制
             * mem_limit:内存限制
             * in_json:{"code":"...","input":"...","cpu_limit":...,"mem_limit":...}
             *
             * out_json:输出型的Json格式的数据，包含字段：
             * status；状态码(必填)
             * reason：结果(必填)
             * stdout：程序运行完的输出结果(选填)
             * stderr：程序运行出错的信息(选填)
             * out_json:{"status":...,"reason":"...","stdout":"...","stderr":"..."}
             */
            // 开始处理
            static void Start(const std::string &in_json, std::string &out_json)
            {
                // 反序列化取出数据
                Json::Value in_value;
                Json::Reader reader;
                reader.parse(in_json, in_value);

                std::string code = in_value["code"].asString();
                std::string input = in_value["input"].asString();
                int cpu_limit = in_value["cpu_limit"].asInt();
                int mem_limit = in_value["mem_limit"].asInt();

                Json::Value out_value;
                std::string filename; // 文件名
                bool ret;
                int status_code = 0; // 状态码
                int run_reasult = 0; // 运行状态码
                if (code.size() == 0)
                {
                    status_code = -1;
                    goto FLAG;
                }
                // 生成唯一的文件名(没有路径和后缀)
                filename = ns_Detail::FileUtil::UniqueFileName();
                // 将code中的内容写入到生成的文件中
                ret = ns_Detail::FileUtil::WriteFile(ns_Detail::PathUtil::Source(filename), code);
                if (!ret)
                {
                    status_code = -2;
                    goto FLAG;
                }
                // 编译代码
                ret = ns_Compiler::Compiler::Compile(filename);
                if (!ret)
                {
                    status_code = -3;
                    goto FLAG;
                }
                // 运行代码
                run_reasult = ns_Runner::Runner::Run(filename, cpu_limit, mem_limit);
                if (run_reasult < 0) // 内部错误
                {
                    status_code = -2;
                }
                else if (run_reasult == 0) // 程序正常运行
                {
                    status_code = 0;
                }
                else // 程序运行异常
                {
                    status_code = run_reasult;
                }

            FLAG:
                out_value["status"] = status_code;
                out_value["reason"] = StatusCodeDesc(status_code,filename);

                if (status_code == 0) // 程序成功编译运行
                {
                    std::string _stdout;
                    ns_Detail::FileUtil::ReadFile(ns_Detail::PathUtil::StdOut(filename),_stdout,true);
                    out_value["stdout"] = _stdout;

                    std::string _stderr;
                    ns_Detail::FileUtil::ReadFile(ns_Detail::PathUtil::StdError(filename),_stderr,true);
                    out_value["stderr"] = _stderr;
                }

                // 序列化输出
                Json::StyledWriter writer;
                out_json = writer.write(out_value);

                //清理临时文件
                RemoveTempFile(filename);
            }
            // 将状态码转化为错误原因
            static std::string StatusCodeDesc(int status_code,const std::string &filename)
            {
                std::string desc;
                switch (status_code)
                {
                case 0:
                    desc = "程序编译运行成功";
                    break;
                case -1:
                    desc = "提交的代码为空";
                    break;
                case -2:
                    desc = "未知错误";
                    break;
                case -3:
                    //desc = "程序编译出错";
                    ns_Detail::FileUtil::ReadFile(ns_Detail::PathUtil::CompileError(filename),desc,true);
                    break;
                case SIGABRT /*6*/:
                    desc = "内存超过限制";
                    break;
                case SIGXCPU /*24*/:
                    desc = "运行超时";
                    break;
                case SIGFPE /*8*/:
                    desc = "除零错误";
                    break;
                default:
                    desc = "未知信号：" + std::to_string(status_code);
                    break;
                }
                return desc;
            }

            //清理临时文件
            static void RemoveTempFile(const std::string& filename)
            {
                std::string _source=ns_Detail::PathUtil::Source(filename);
                if(ns_Detail::FileUtil::IsFileExists(_source))
                {
                    unlink(_source.c_str());
                }

                std::string _comp_err=ns_Detail::PathUtil::CompileError(filename);
                if(ns_Detail::FileUtil::IsFileExists(_comp_err))
                {
                    unlink(_comp_err.c_str());
                }

                std::string _exe=ns_Detail::PathUtil::Exe(filename);
                if(ns_Detail::FileUtil::IsFileExists(_exe))
                {
                    unlink(_exe.c_str());
                }

                std::string _stdin=ns_Detail::PathUtil::StdIn(filename);
                if(ns_Detail::FileUtil::IsFileExists(_stdin))
                {
                    unlink(_stdin.c_str());
                }

                std::string _stdout=ns_Detail::PathUtil::StdOut(filename);
                if(ns_Detail::FileUtil::IsFileExists(_stdout))
                {
                    unlink(_stdout.c_str());
                }

                std::string _stderr=ns_Detail::PathUtil::StdError(filename);
                if(ns_Detail::FileUtil::IsFileExists(_stderr))
                {
                    unlink(_stderr.c_str());
                }
            }
        };
    }
}